package algorithm;

import DataStructure.CharStack;
import DataStructure.Tree;
import DataStructure.TreeNode;
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Field;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import util.LagrangeSet;
import util.PolyItem;
import util.Polynomial;
import util.Symbol;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class CPABEdec {
    private Tree tree;
//    private BigInteger order;

    public String decrypt(CPABECT ct, CPABEAttKey attKey) throws Exception {
        if(ct.getTree()==null){
            parsePolicy(ct.getPolicy());
            ct.setTree(tree);
        }
        Pairing pairing= PairingFactory.getPairing("params.properties");
        int rootTag=ct.getTree().getRootIndex();
        Element a = decryptNode(ct, attKey, ct.getTree().getNode(rootTag), pairing);
        Element dUp=pairing.pairing(ct.getC(),attKey.getD()).getImmutable();
        Element down=dUp.div(a).getImmutable();
        Element tResult=ct.getCm().div(down).getImmutable();
        String result=tResult.toBigInteger().toString();
        return result;

    }

    private Element decryptNode(CPABECT ct, CPABEAttKey attKey, TreeNode x,Pairing pairing) throws Exception {
        Field zn=pairing.getZr();
        Element temp=pairing.getGT().newOneElement();
        Element[] reData=new Element[x.getOrder()];
        int[] children=x.getChildren();
        if(x.getAttribute()==null){
            //非叶节点
            for (int i = 0; i < children.length; i++) {
                reData[i]=decryptNode(ct,attKey,ct.getTree().getNode(children[i]),pairing);
            }
            if(x.getThreshold()==1){
                for (int i = 0; i < reData.length; i++) {
                    if(reData[i]!=null){
                        return reData[i];
                    }
                }
                return null;
            }else{
                for (int i = 0; i < reData.length; i++) {
                    if(reData[i]!=null){
                        BigInteger lagPolyValue2=LagrangeSet.larBasicPoly(BigDecimal.ZERO,
                                getIndexSet(x,ct),
                                BigDecimal.valueOf(ct.getTree().getNode(children[i]).getIndex()),
                                i).toBigInteger();
                        temp=temp.mul(reData[i].powZn(zn.newElement(lagPolyValue2).getImmutable()));
                    }else{
                        return null;
                    }
                }
                return temp;
            }
        }else{
            //对应叶子节点的情况
            //检查节点属性是否在属性列表中
            String[] attList=attKey.getAttlist();
            EAUnion tempUnion1;
            EAUnion tempUnion2;
            for (int i = 0; i < attList.length; i++) {
                if(x.getAttribute().equals(attList[i])){
                    //节点的属性在属性列表当中
                    tempUnion1=searchItemByAttribute(attKey.getDj1(),attList[i]);
                    tempUnion2=searchItemByAttribute(ct.getCy1(),attList[i]);
                    Element up=pairing.pairing(tempUnion1.geteData(),tempUnion2.geteData()).getImmutable();
                    tempUnion1=searchItemByAttribute(attKey.getDj2(),attList[i]);
                    tempUnion2=searchItemByAttribute(ct.getCy2(),attList[i]);
                    Element down=pairing.pairing(tempUnion1.geteData(),tempUnion2.geteData()).getImmutable();
                    Element result=up.div(down).getImmutable();
                    return result;
                }
            }
            return null;
        }
    }

    private EAUnion searchItemByAttribute(EAUnion[] unions,String attribute){
        for (int i = 0; i < unions.length; i++) {
            if(unions[i].getAttribute().equals(attribute)){
                return unions[i];
            }
        }
        return null;
    }

    /**
     * 获得一个节点所有子节点的index的值
     * @param x
     * @param ct
     * @return
     */
    private BigDecimal[] getIndexSet(TreeNode x,CPABECT ct){
        BigDecimal[] reValue=new BigDecimal[x.getOrder()];
        int[] children=x.getChildren();
        for (int i = 0; i < children.length; i++) {
            reValue[i]=BigDecimal.valueOf(
                    ct.getTree().getNode(children[i]).getIndex()
            );
        }
        return reValue;
    }


    /**
     * 构建策略树
     * @param policy 用户输入的策略
     * @throws Exception 策略输入格式有问题
     */
    public void parsePolicy(String policy) throws Exception {
        if (policy.length()==0){
            throw new Exception("策略不能为空/policy can`t be empty");
        }
        //初始化策略树
        List<String> rPolishType=reversePolishType(policy.toCharArray());
        String outData=listOutput(rPolishType);
        System.out.println("策略策略解析结果为："+outData);
        buildBinaryTree(rPolishType);
    }

    /**
     * 将用户输入的策略转变为逆波兰表达式，为建树做准备
     * 并且能够检测出策略格式是否有问题
     * 目前支持的检测点为：括号使用不当
     * @param expression 策略的字符数组，string数组的时候采用UTF-8编码
     * @return 逆波兰式中的每一项
     * @throws Exception 输入策略格式有问题
     */
    private List<String> reversePolishType(char[] expression) throws Exception {
        //TODO-pht:减少栈的使用量
        CharStack<Symbol> symbolStack = new CharStack<Symbol>();
        CharStack<Character> dataStack = new CharStack<Character>();
        List<String> output = new ArrayList<>();
        //output当中的的游标
        int tag = 0;
        //放入队列中的数据
        String inData;
        for (int i = 0; i < expression.length; i++) {
            Symbol temporary=null;
            //处理数据与运算符的关系
            switch (expression[i]) {
                case '(':
                    //左边一定是运算符，直接压入
                    Symbol leftBracket = new Symbol('(', getPriority('('));
                    symbolStack.push(leftBracket);
                    //左括号优先级最大，不需要做优先级检查
                    break;
                case ')':
                    //左边一定是数，将数据放入输出队列中
                    if (!dataStack.isEmpty()) {
                        inData = dataStack.getString();
                        output.add(tag++, inData);
                    }
                    //将运算符压入运算符栈中
                    Symbol rightBracket = new Symbol(')', getPriority(')'));
                    symbolStack.push(rightBracket);
                    //优先级检查
                    temporary=handlePriority(symbolStack);
                    while(temporary!=null){
                        inData=String.valueOf(temporary.getSym());
                        output.add(tag++,inData);
                        temporary=handlePriority(symbolStack);
                    }
                    break;
                case '*':
                    //压入运算符
                    Symbol starKey = new Symbol('*', getPriority('*'));
                    symbolStack.push(starKey);
                    if (!dataStack.isEmpty()) {
                        inData = dataStack.getString();
                        output.add(tag++, inData);
                    }
                    //优先级检查
                    temporary=handlePriority(symbolStack);
                    while(temporary!=null){
                        inData=String.valueOf(temporary.getSym());
                        output.add(tag++,inData);
                        temporary=handlePriority(symbolStack);
                    }
                    break;
                case '#':
                    //压入运算符
                    Symbol hashKey = new Symbol('#', getPriority('#'));
                    symbolStack.push(hashKey);
                    if(!dataStack.isEmpty()){
                        inData=dataStack.getString();
                        output.add(tag++,inData);
                    }
                    //优先级检查
                    temporary=handlePriority(symbolStack);
                    while(temporary!=null){
                        inData=String.valueOf(temporary.getSym());
                        output.add(tag++,inData);
                        temporary=handlePriority(symbolStack);
                    }
                    break;
                default:
                    //将数据添加到数据栈中
                    dataStack.push(expression[i]);
                    break;
            }
        }
        //判断数据栈中是否为空，不为空则直接加入后续的输出当中
        if(!dataStack.isEmpty()){
            inData=dataStack.getString();
            output.add(tag++,inData);
        }
        //符号栈不为空，处理后续的东西
        //首先判断输入是否合法,同时生成符号的数组
        int stackSize=symbolStack.size();
        Symbol[] symbolList=new Symbol[stackSize];
        for (int i = 0; i < stackSize; i++) {
            symbolList[i]=symbolStack.pop();
            if(symbolList[i].getSym()==')'||symbolList[i].getSym()=='('){
                throw new  Exception("策略格式错误/wrong form of policy");
            }
        }
        //如果只有一个符号，则直接输出
        if(symbolList.length==1){
            output.add(tag++,String.valueOf(symbolList[0].getSym()));
        }else{
            //对符号数组按照优先级从高到底的顺序排列
            for (int i = 0; i <symbolList.length ; i++) {
                int maxtag=i;
                int maxPriority=symbolList[i].getPriority();
                for (int j = i+1; j < symbolList.length; j++) {
                    if(symbolList[j].getPriority()>maxPriority){
                        maxtag=j;
                        maxPriority=symbolList[j].getPriority();
                    }
                }
                Symbol temp=symbolList[i];
                symbolList[i]=symbolList[maxtag];
                symbolList[maxtag]=temp;
            }
            //将多出来的运算符添加到输出列表当中
            for (int i = 0; i < symbolList.length; i++) {
                inData=String.valueOf(symbolList[i].getSym());
                output.add(tag++,inData);
            }
        }
        return output;
    }

    private int getPriority(char symbol) {
        switch (symbol) {
            case '(':
                return 4;
            case '*':
                return 3;
            case '#':
                return 2;
            case ')':
                return 1;
            default:
                return 0;
        }
    }

    /**
     * 当符号入栈时进行优先级检查，并做弹出操作
     */
    private Symbol handlePriority(CharStack<Symbol> symbolStack){
        if(symbolStack.size()<=1){
            return null;
        }

        Symbol top=symbolStack.pop();
        Symbol down=symbolStack.pop();
        if(top.getPriority()>down.getPriority()){
            //当前插入的运算符的优先级大于前一个的时候，将两个符号按照顺序压回队列
            symbolStack.push(down);
            symbolStack.push(top);
        }else{
            //
            if(!(down.getSym()=='('||top.getSym()==')')){
                symbolStack.push(top);
                return down;
            }
            if(down.getSym()=='('&&top.getSym()!=')') {
                symbolStack.push(down);
                symbolStack.push(top);
            }
            if(down.getSym()!='('&&top.getSym()==')'){
                symbolStack.push(top);
                return down;
            }
            if(down.getSym()=='('&&top.getSym()==')'){
                return null;
            }
        }
        return null;
    }

    /**
     * 将输入的List中的各个字符串按照先后顺序排组合成字符串
     * @param listData 输入的list
     * @return 组合成的字符串
     */
    private String listOutput(List<String> listData){
        //组成输出字符串
        String outData="";
        for (int i = 0; i < listData.size(); i++) {
            outData=outData+listData.get(i);
        }
        return outData;
    }

    /**
     * 通过输入的逆波兰表达式的各个项，构建策略树
     * @param rPolishTye 逆波兰表达式的各个项
     */
    private void buildBinaryTree(List<String> rPolishTye){
        tree=new Tree();
        //如果只有一个节点的话
        if(rPolishTye.size()==1){
            TreeNode root=new TreeNode();
            //符号节点
            int[] child=new int[1];
            root.setChildren(child);
            root.setOrder(1);
            root.setThreshold(root.getOrder());
            //设置运算符子节点
            TreeNode onlyOne=new TreeNode(0,1,0,null);
            onlyOne.setChildren(null);
            onlyOne.setParent(1);
            onlyOne.setViewed(false);
            onlyOne.setAttribute(rPolishTye.get(0));
            root.setParent(-2);
            root.setNodeIndex(1);
            root.setAttribute(null);
            root.setViewed(false);
            tree.insertNode(onlyOne,onlyOne.getNodeIndex());
            tree.insertNode(root,root.getNodeIndex());

            //当只有一个属性节点的时候，根节点就是这个与运算节点
            tree.setRootIndex(1);
            fullFillTree();
            return;
        }
        //有多个节点的情况
        for (int i = 0; i < rPolishTye.size(); i++) {
            String data=rPolishTye.get(i);
            switch (getState(data)){
                case 1:
                    //符号节点
                    TreeNode nodeSymbol=new TreeNode();
                    int[] tuple=getNoParentList(tree,i);
                    nodeSymbol.setOrder(tuple[2]);
                    if(rPolishTye.get(i).equals("#")){
                        //或门，门限值为1
                        nodeSymbol.setThreshold(1);
                    }else{
                        //与门,门限值为阶的值
                        nodeSymbol.setThreshold(nodeSymbol.getOrder());
                    }
                    int[] children=new int[nodeSymbol.getOrder()];
                    //设置运算符子节点
                    for (int j = 0; j < children.length; j++) {
                        children[j]=tuple[j];
                    }
                    nodeSymbol.setChildren(children);
                    nodeSymbol.setParent(-2);
                    nodeSymbol.setNodeIndex(i);
                    nodeSymbol.setAttribute(null);
                    nodeSymbol.setViewed(false);
                    //修改子节点的父节点编号
                    int[] kid=nodeSymbol.getChildren();
                    for (int j = 0; j < nodeSymbol.getOrder(); j++) {
                        tree.getNode(kid[j]).setParent(nodeSymbol.getNodeIndex());
                    }
                    tree.insertNode(nodeSymbol,nodeSymbol.getNodeIndex());
                    break;
                case 0:
                    //属性节点
                    TreeNode nodeData=new TreeNode();
                    nodeData.setOrder(0);
                    nodeData.setThreshold(1);
                    nodeData.setChildren(null);
                    //-2表示还没有设置父节点
                    nodeData.setParent(-2);
                    nodeData.setNodeIndex(i);
                    nodeData.setAttribute(rPolishTye.get(i));
                    nodeData.setViewed(false);
                    tree.insertNode(nodeData,nodeData.getNodeIndex());
                    break;
            }
        }
        //当有多个节点的时候，由逆波兰式的特点所决定，最后一个进入树的节点一定为根节点
        tree.setRootIndex(rPolishTye.size()-1);
        fullFillTree();
    }

    /**
     * 获得目前所有树节点中没有父节点的节点，用来建立父子节点之间的关系
     * @param tree 树
     * @param tag 目前节点的标号
     * @return int数组，size为3，第一个是右孩子节点的标号，第二个是左孩子节点的标号，
     */
    private int[] getNoParentList(Tree tree,int tag){
        //第一个值是左孩子节点的位置，第二个值是右孩子节点的位置，第三个值是有几个孩子
        int[] tuple=new int[3];
        int size=0;
        int index=0;
        for (int i = tag-1; i >=0; i--) {
            if(tree.getNode(i).getParent()==-2){
                //没有设置父节点
                tuple[index]=i;
                index++;
                size++;
                if(size==2){
                    break;
                }
            }
        }
        tuple[2]=size;
        return  tuple;
    }

    /**
     * 获取上一个节点的状态值
     * @param part 状态
     * @return 状态值
     */
    private int getState(String part){
        switch (part){
            case "(":
                return 1;
            case ")":
                return 1;
            case "#":
                return 1;
            case "*":
                return 1;
            default:
                return 0;
        }
    }

    /**
     * 在树由超过两个节点构成的时候，填充每一个节点在CPABE算法种设定的值
     * 该函数调用另外一个递归函数来完成该操作。
     */
    private void fullFillTree(){
        //TODO-pht:将填充多项式用的树改为Element类型

        //填充父节点的值
        TreeNode root=tree.getNode(tree.getRootIndex());
        root.setIndex(1);
        //填充余下所有节点的值
        int[] children=root.getChildren();
        if(children.length==2){
            fullNode(tree.getNode(children[0]),1);
            fullNode(tree.getNode(children[1]),2);
        }else if(children.length==1){
            fullNode(tree.getNode(children[0]),1);
        }
    }

    /**
     * 递归填充一个节点的子节点
     * @param node
     */
    private void fullNode(TreeNode node,int index){
        //TODO-pht:修改为非递归版本
        node.setIndex(index);
//        BigInteger constant=BigInteger.valueOf(tree.getNode(node.getParent()).getIndex());

//        constant=constant.mod(order);
        //当没有子节点的时候退出递归
        if(node.getChildren()==null){
            return;
        }
        int[] childre=node.getChildren();
        if(childre.length==2){
            fullNode(tree.getNode(childre[0]),0);
            fullNode(tree.getNode(childre[1]),1);
        }else if(childre.length==1){
            fullNode(tree.getNode(childre[0]),0);
        }
    }

}

